from django import forms
from app import models
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
import random
from drf import settings
from utils.sms import send_sms_single
from django_redis import get_redis_connection
from utils import encrypt

class BootstrapForm():
    bootstrap_class_exclude = []
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for name, field in self.fields.items():
            if name in self.bootstrap_class_exclude:
                continue
            field.widget.attrs['class'] = 'form-control'
            field.widget.attrs['placeholder'] = f'请输入{field.label}'


class RegisterModelForm(BootstrapForm,forms.ModelForm):
    password = forms.CharField(
        label='密码',
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': "密码长度不能小于8个字符",
            'max_length': "密码长度不能大于64个字符"
        },
        widget=forms.PasswordInput())
    confirm_password=forms.CharField(
        label='重复密码',
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': "密码长度不能小于8个字符",
            'max_length': "密码长度不能大于64个字符"
        },
        widget=forms.PasswordInput())
    phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
    code = forms.CharField(label='验证码',widget=forms.TextInput())
    class Meta:
        model = models.User
        fields = ['username','password','confirm_password','email','phone','code']

    def clean_username(self):
        username = self.cleaned_data['username']
        exists = models.User.objects.filter(username=username).exists()
        if exists:
            raise ValidationError('用户名已存在')
        return username

    def clean_password(self):
        password = self.cleaned_data['password']
        return encrypt.md5(password)

    def clean_confirm_password(self):
        pwd = self.cleaned_data.get('password')
        confirm_password = self.cleaned_data['confirm_password']
        if encrypt.md5(confirm_password) == pwd:
            pass
        else:
            raise ValidationError('密码前后不一致')
        return confirm_password

    def clean_email(self):
        email = self.cleaned_data['email']
        exist = models.User.objects.filter(email=email).exists()
        if exist:
           raise ValidationError('邮箱已存在')
        return email

    def clean_phone(self):
        phone = self.cleaned_data['phone']
        exist = models.User.objects.filter(phone=phone).exists()
        if exist:
            raise ValidationError('手机号已存在')
        return phone

    def clean_code(self):
        code = self.cleaned_data['code']
        phone = self.cleaned_data.get('phone')
        if not phone:  #通过get的方式，如果phone字段没有通过校验则直接返回code，否则报KeyError
            return code
        conn = get_redis_connection()
        redis_code = conn.get(phone)
        if not redis_code:
            raise ValidationError('验证码失效或未发送，请重新发送')
        redis_str_code = redis_code.decode('utf-8')
        if code.strip() != redis_str_code:
            raise ValidationError('验证码错误，请重新输入')
        return code



class SendSmsForm(forms.Form):
    phone = forms.CharField(label='手机号',validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_phone(self):
        """手机号校验的钩子"""
        phone = self.cleaned_data['phone']
        # 判断短信模板是否有问题
        tpl = self.request.POST.get('tpl')
        template_id = settings.TENCENT_SMS_TEMPLATE.get(tpl)
        if not template_id:
            # self.add_error('phone','短信模板错误')
            raise ValidationError('短信模板错误')

        exists = models.User.objects.filter(phone=phone).exists()
        if tpl == 'login':   #如果用户行为是登录则判断用户是否没有存在于数据库
            if not exists:
                raise ValidationError('手机号不存在')
        else:
            # 校验数据库中是否已有手机号
            if exists:
                raise ValidationError('手机号已存在')

        code = random.randrange(1000, 9999)

        # 发送短信
        sms = send_sms_single(phone, template_id, [code, 1])
        if sms['result'] != 0:
            raise ValidationError("短信发送失败，{}".format(sms['errmsg']))

        # 验证码 写入redis（django-redis）
        conn = get_redis_connection()
        conn.set(phone, code, ex=60)

        return phone


class LoginSmsForm(BootstrapForm,forms.Form):
    phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
    code = forms.CharField(label='验证码', widget=forms.TextInput())

    def clean_phone(self):
        phone = self.cleaned_data['phone']
        user_obj = models.User.objects.filter(phone=phone).first()
        if not user_obj:
            raise ValidationError('手机号不存在')
        return user_obj
    def clean_code(self):
        code = self.cleaned_data['code']
        user_obj = self.cleaned_data.get('phone')
        #若手机号没有存在则返回验证码
        if not user_obj:
            return code
        conn = get_redis_connection()
        redis_code = conn.get(user_obj.phone)
        if not redis_code:
            raise ValidationError('验证码失效或未发送，请重新发送')

        redis_str_code = redis_code.decode('utf-8')

        if code.strip() != redis_str_code:
            raise ValidationError('验证码错误')
        return code

class LoginForm(BootstrapForm,forms.Form):
    username = forms.CharField(label='邮箱或手机号')
    password = forms.CharField(label='密码',widget=forms.PasswordInput(render_value=True))
    code = forms.CharField(label='验证码')

    def __init__(self,request,*args,**kwargs):
        super().__init__(*args,**kwargs)
        self.request = request

    def clean_password(self):
        password = self.cleaned_data['password']
        print('--->',password)
        return encrypt.md5(password)

    def clean_code(self):
        code = self.cleaned_data['code']
        session_code = self.request.session.get('login_code')
        if not session_code:
            raise ValidationError('验证码已过期，请重新获取')
        if code.strip().lower() != session_code.lower():
            raise ValidationError('验证码输入有误！')
        return code